/*
  Program: Mine Mayhem
  File:    fieldsqr.cc
  Date:    July, 1996 to February,   1997,    version 1.00
	   26 October to 3 December,	      version 2.00
	   8 December to 4 January,  2001,    version 2.10
	   15 January,			      version 2.11
	   18 September to 16 December, 2006, version 2.90

  Everything to do with the rectangular minefield.

  Freeware, Copyright 2006 Jason Hood.
*/

#include "minefld.h"
#include "options.h"
#include "tiles.h"
#include "board.h"

#include <stdlib.h>


SquareField::SquareField(bool create) : Minefield()
{
  Beginner     = Layout( 9,  9, 10);
  Intermediate = Layout(16, 16, 40);
  Expert       = Layout(30, 16, 99);

  MinWidth = MinHeight = 5;

  if (create)
  {
    MaxWidth  = max_cols = max_width / SQUAREW;
    MaxHeight = max_rows = max_height / SQUAREH;
    CreateMinefield();
  }
}


/* -----------------------------   EveryTile   ------------------------------

  Create the list of squares on the minefield.

*/

void SquareField::EveryTile()
{
  int p;
  int w = layout.dim.x - 1,
      h = layout.dim.y - 1;

  cols = layout.dim.x;
  rows = layout.dim.y;

  p = tile_count = 0;
  for (int y = 0; y < layout.dim.y; y++)
  {
    for (int x = 0; x < layout.dim.x; x++)
    {
      field[tile_count].grid.x = x;
      field[tile_count].grid.y = y;
      field[tile_count].loc.x  = x * SQUAREW;
      field[tile_count].loc.y  = y * SQUAREH;

      grid[y][x] = &field[tile_count];

      if (y == 0 || y == h || x == 0 || x == w)
	perimeter[p++] = tile_count;

      ++tile_count;
    }
  }
  perimeter[p] = -1;

  foralltiles
    Surround(&field[tile]);

  pan_wid = (layout.dim.x - 1) * SQUAREW;
}


/* -----------------------------   AdjustWrap	-----------------------------

  When the Wrap-around option is changed the edges need to be re-surrounded.

*/

void SquareField::AdjustWrap()
{
  for (int* tile = perimeter; *tile >= 0; ++tile)
    Surround(&field[*tile]);
}


/* ------------------------------   Surround   ------------------------------

  Determine the surrounding squares, where the order is:

    8 6 4
    7 0 2
    5 3 1

  This is to reduce opening squares in the cross game.

*/

void SquareField::Surround(Tile* t)
{
  static cCoord ofs[] = { Coord( 0, 0), Coord(+1,+1), Coord(+1, 0),
			  Coord( 0,+1), Coord(+1,-1), Coord(-1,+1),
			  Coord( 0,-1), Coord(-1, 0), Coord(-1,-1) };

  Minefield::Surround(t, 9, ofs);
}


/* ------------------------------   add/cmp   -------------------------------

  When using wrap, simply adding and comparing co-ordinates won't work.
  These functions will correctly account for wrap to ensure valid results
  (assuming valid input - adding more than the maximum will not wrap twice).

*/

static int add(int x, int dx, int wrap, int max)
{
  x += dx;
  if (wrap)
  {
    if (x < 0)
      x += max;
    else if (x >= max)
      x -= max;
  }
  return x;
}

int SquareField::addx(int x, int dx) const
{
  return add(x, dx, canWrap(), layout.dim.x);
}

int SquareField::addy(int y, int dy) const
{
  return add(y, dy, canWrap(), layout.dim.y);
}


// When wrapping, compare using the least distance.  Eg: if the field is 9
// across and x1 is 2 and x2 is 7, say that 2 (3, 4, 5, 6, 7 = 5 tiles) is
// bigger than 7 (8, 0, 1, 2 = 4 tiles).
static int cmp(int x1, int x2, int wrap, int max)
{
  if (!wrap || abs(x1 - x2) <= max / 2)
    return x1 - x2;

  if (x1 > x2)
    return (x1 - max) - x2;
  else
    return (x1 + max) - x2;
}

int SquareField::cmpx(int x1, int x2) const
{
  return cmp(x1, x2, canWrap(), layout.dim.x);
}

int SquareField::cmpy(int y1, int y2) const
{
  return cmp(y1, y2, canWrap(), layout.dim.y);
}


/* --------------------------------   Area   --------------------------------

  Area of a rectangle - anyone not know this?

*/

int SquareField::area(cCoord& dim) const
{
  return (dim.x * dim.y);
}


/* -----------------------------   RandomDim   ------------------------------

  Generate a random dimension.

*/

void SquareField::RandomDim()
{
  Random.dim.x = random() % (MaxWidth  - MinWidth  + 1) + MinWidth;
  Random.dim.y = random() % (MaxHeight - MinHeight + 1) + MinHeight;
}


/* -------------------------   GetBoardDimensions   -------------------------

  Given the dimensions of the minefield, return the dimensions of the board.

*/

Coord SquareField::GetBoardDimensions(cCoord& dim)
{
  return Coord(dim.x * SQUAREW, dim.y * SQUAREH);
}


/* ------------------------------   Outline   -------------------------------

  Draw the outline around the minefield.

*/

void SquareField::Outline(ViewBuffer* buf, int x1, int y1, int x2, int y2)
{
  VLine(buf, x2, y1, y2, 149);		// 149 is grey
  HLine(buf, x1, x2, y2, 149);
  HLine(buf, x1, x2, y1, 235);		// 235 is white
  VLine(buf, x1, y1, y2, 235);
}


/* ------------------------------   Convert   -------------------------------

  Convert the mouse co-ordinate to a grid co-ordinate.

*/

void SquareField::Convert(int& x, int& y)
{
  x /= SQUAREW;
  y /= SQUAREH;
}


/* ----------------------------   DisplayTile	-----------------------------

  Display the square t using which bitmap.

*/

void SquareField::DisplayTile(Tile* t, int which)
{
  Minefield::DisplayTile(t, which, SQUAREW-1, SQUAREH-1, Square);
}


/* ------------------------------   Validate   ------------------------------

  Ensure the layout does not exceed the limits.

*/

void SquareField::Validate(Layout& layout)
{
  if (layout.dim.x < MinWidth)
    layout.dim.x = MinWidth;
  else if (layout.dim.x > MaxWidth)
    layout.dim.x = MaxWidth;

  if (layout.dim.y < MinHeight)
    layout.dim.y = MinHeight;
  else if (layout.dim.y > MaxHeight)
    layout.dim.y = MaxHeight;

  Minefield::Validate(layout);
}


/* -------------------------   ReadIni & WriteIni   -------------------------

  As you can see, it uses the "[ Squares ]" section and times are kept in
  the "minesqr.tym" file.

*/

void SquareField::ReadIni(IniFile& ini)
{
  Minefield::ReadIni(ini, "Squares", "minesqr.tym");
}

void SquareField::WriteIni(IniFile& ini)
{
  Minefield::WriteIni(ini, "Squares", "minesqr.tym");
}
